﻿/**
* CRL
*/
using CRL.Data.LambdaQuery;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CRL.Data.DBExtend.RelationDB
{
	public sealed partial class DBExtend
	{
		/// <summary>
		/// 格式化为更新值查询
		/// </summary>
		/// <param name="setValue"></param>
		/// <param name="joinType"></param>
		/// <returns></returns>
		string ForamtSetValue<T>(ParameCollection setValue, Type joinType = null)
		{
			//string tableName = TypeCache.GetTableName(typeof(T), dbContext);
			string setString = "";
			var fields = TypeCache.GetProperties(typeof(T), true);
			foreach (var pair in setValue)
			{
				string name = pair.Key;
				object value = pair.Value;

				value = ObjectConvert.CheckNullValue(value);

				if (name.StartsWith("$"))//直接按值拼接 c2["$SoldCount"] = "SoldCount+" + num;
				{
					name = name.Substring(1, name.Length - 1);
					if (!fields.ContainsKey(name))
					{
						throw new Exception("找不到对应的字段,在" + typeof(T) + ",名称" + name);
					}
					var field = fields[name];
					string value1 = value.ToString();
					//未处理空格
					value1 = System.Text.RegularExpressions.Regex.Replace(value1, name + @"([\+\-])", _DBAdapter.FieldNameFormat(field) + "$1", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
					name = field.MapingName;
					value = value1;
					setString += string.Format("{0}={1},", _DBAdapter.KeyWordFormat(name), value);
				}
				else
				{
					if (joinType != null && value.ToString().Contains("$"))//当是关联更新
					{

						if (!fields.ContainsKey(name))
						{
							throw new Exception("找不到对应的字段,在" + typeof(T) + ",名称" + name);
						}
						var field = fields[name];
						name = field.MapingName;//转换映射名

						var fields2 = TypeCache.GetProperties(joinType, true);
						var value1 = System.Text.RegularExpressions.Regex.Match(value.ToString(), @"\$(\w+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Groups[1].Value;
						if (!fields2.ContainsKey(value1))
						{
							throw new Exception("找不到对应的字段,在" + joinType + ",名称" + value1);
						}
						var field2 = fields2[value1];
						value = value.ToString().Replace("$" + value1, "t2." + _DBAdapter.KeyWordFormat(field2.MapingName));//右边字段需加前辍
						name = string.Format("t1.{0}", _DBAdapter.KeyWordFormat(name));
					}
					else
					{
						if (!fields.ContainsKey(name))
						{
							throw new Exception("找不到对应的字段,在" + typeof(T) + ",名称" + name);
						}
						var field = fields[name];
						if (field.ValueNeedConvert)
						{
							value = SettingConfig.ValueObjSerializer(value);
						}
						name = field.MapingName;//转换映射名
						var parame = _DBAdapter.GetParamName(name, dbContext.parIndex);
						AddParame(parame, value);
						dbContext.parIndex += 1;
						value = parame;
						if (joinType != null)//mysql 修正
						{
							name = string.Format("t1.{0}", _DBAdapter.KeyWordFormat(name));
						}
						else
						{
							name = _DBAdapter.KeyWordFormat(name);
						}
					}
					setString += string.Format("{0}={1},", name, value);

				}
			}
			setString = setString.Substring(0, setString.Length - 1);
			return setString;
		}
		#region update


		/// <summary>
		/// 指定拼接条件更新
		/// </summary>
		/// <typeparam name="TModel"></typeparam>
		/// <param name="setValue"></param>
		/// <param name="where"></param>
		/// <returns></returns>
		internal int Update<TModel>(ParameCollection setValue, string where)
		{
			CheckTableCreated<TModel>();
			Type type = typeof(TModel);
			string table = TypeCache.GetTableName(type, dbContext);
			string setString = ForamtSetValue<TModel>(setValue);
			string sql = _DBAdapter.GetUpdateSql(table, setString, where);
			sql = _DBAdapter.SqlFormat(sql);
			var db = GetDBHelper();
			var n = SqlStopWatch.Execute(db, sql);
			ClearParame();
			return n;
		}

		/// <summary>
		/// 按完整查询条件进行更新
		/// goup语法不支持,其它支持
		/// </summary>
		/// <typeparam name="TModel"></typeparam>
		/// <param name="query"></param>
		/// <param name="updateValue"></param>
		/// <returns></returns>
		public override int Update<TModel>(ILambdaQuery<TModel> query, ParameCollection updateValue)
		{
			var query1 = query as RelationLambdaQuery<TModel>;
			if (query1.__GroupFields != null)
			{
				throw new Exception("update不支持group查询");
			}
			if (query1.__Relations != null && query1.__Relations.Count > 1)
			{
				throw new Exception("update关联不支持多次");
			}
			if (updateValue.Count == 0)
			{
				throw new ArgumentNullException("更新时发生错误,参数值为空 ParameCollection setValue");
			}

			var conditions = Core.StringBuilderCache.GetSimpleString(sb =>
			{
				query1.GetQueryConditions(sb, false);
			}).Trim();

			query1.FillParames(this);

			if (query1.__Relations != null)
			{
				var kv = query1.__Relations.First();
				string setString = ForamtSetValue<TModel>(updateValue, kv.Key.OriginType);
				var t1 = query1.QueryTableName;
				var t2 = TypeCache.GetTableName(kv.Key.OriginType, query1.__DbContext);

				string sql = _DBAdapter.GetRelationUpdateSql(t1, t2, conditions, setString, query1);
				return Execute(sql);
			}
			else
			{
				conditions = conditions.Replace("t1.", "");
			}
			return Update<TModel>(updateValue, conditions);
		}
		int MaxSqlLength => 1024 * 100;
		public override int Update<TModel>(List<TModel> objs)
		{
			if (!objs.Any())
			{
				return 0;
			}
			var table = TypeCache.GetTable(typeof(TModel));
			var primaryKey = table.PrimaryKey;
			var batchUpdate = new BatchUpdate<TModel>();

			foreach (var obj in objs)
			{
				ParameCollection c;
				var trackingDiff = GetTrackingDiff(obj);
				if (trackingDiff.Any())
				{
					c = trackingDiff;
				}
				else
				{
					c = GetUpdateField(obj);
				}

				if (c.Count == 0)
				{
					continue;
				}
				var updateItem = batchUpdate.CreateItem();
				updateItem.updateValue = c;
				var keyValue = primaryKey.GetValue(obj);
				updateItem.AddCondition(primaryKey.MemberName, keyValue);
			}
			return Update(batchUpdate);
		}

		public override int Update<TModel>(BatchUpdate<TModel> batchUpdate)
		{
			IEnumerable<BatchUpdateItem> batchUpdates = batchUpdate.items;
			if (!batchUpdates.Any())
			{
				return 0;
			}
			var effectRows = 0;
			var modelType = typeof(TModel);
			var table = TypeCache.GetTable(modelType);
			var tableName = _DBAdapter.KeyWordFormat(table.TableName);
			var primaryKey = table.PrimaryKey;
			string formatFieldName(string memberName)
			{
				table.FieldsDic.TryGetValue(memberName, out var attr);
				if (attr == null)
				{
					throw new Exception($"未找到对象{modelType.Name}属性{memberName}");
				}
				return _DBAdapter.KeyWordFormat(attr.MapingName);
			}
			var db = GetDBHelper();
			var updateFileds = batchUpdates.FirstOrDefault().updateValue.Keys.ToList();
			var conditionFileds = batchUpdates.FirstOrDefault().condition.Keys.ToList();
			var updateFiledsStr = string.Join(",", updateFileds.Select(b => $"t1.{formatFieldName(b)}=t2.{formatFieldName(b)}"));
			var conditionFiledsStr = string.Join(" and ", conditionFileds.Select(b => $"t1.{formatFieldName(b)}=t2.{formatFieldName(b)}"));
			string convertValue(object value)
			{
				if (value == null)
				{
					return "null";
				}
				var v = value.ToString();
				if (v.Contains("'"))
				{
					v = v.Replace("'", "＇");
				}
				return $"'{v}'";
			}
			bool checkFieldCount(Dictionary<string, object> dic1, Dictionary<string, object> dic2)
			{
				var key1 = string.Join(",", dic1.Keys.ToArray());
				var key2 = string.Join(",", dic2.Keys.ToArray());
				return key1 == key2;
			}
			var nextPage = false;
			var totalIndex = 0;
			var first = batchUpdates.First();
		label1:
			int index = 0;
			var t2 = new StringBuilder("(");
			foreach (var obj in batchUpdates)
			{
				if (!obj.condition.Any())
				{
					throw new Exception("缺少条件condition");
				}
				string selectValue, conditionSelect;
				if (index == 0)
				{
					selectValue = string.Join(",", obj.updateValue.Select(b => $"{convertValue(b.Value)} as {formatFieldName(b.Key)}"));
					conditionSelect = string.Join(",", obj.condition.Select(b => $"{convertValue(b.Value)} as {formatFieldName(b.Key)}"));
				}
				else
				{
					if (!checkFieldCount(first.updateValue, obj.updateValue))
					{
						throw new Exception("项更新字段不一致");
					}
					if (!checkFieldCount(first.condition, obj.condition))
					{
						throw new Exception("项条件字段不一致");
					}
					selectValue = string.Join(",", obj.updateValue.Select(b => convertValue(b.Value)));
					conditionSelect = string.Join(",", obj.condition.Select(b => convertValue(b.Value)));
				}
				if (!string.IsNullOrEmpty(selectValue))
				{
					selectValue += ",";
				}
				t2.Append($"select {selectValue}{conditionSelect}");
				index++;
				totalIndex++;
				if (t2.Length > MaxSqlLength)
				{
					nextPage = true;
				}
				if (_DBAdapter.DBType == DBAccess.DBType.SQLITE && index >= 500)
				{
					nextPage = true;
				}
				if (nextPage)
				{
					break;
				}
				t2.Append(" union all \r\n");
			}
			t2.Append($")");
			var selectResult = t2.ToString();
			selectResult = selectResult.Replace("union all \r\n)", ")");
			var sql = _DBAdapter.GetRelationUpdateSql(table.TableName, selectResult, conditionFiledsStr, updateFiledsStr, null);
			var n = SqlStopWatch.Execute(db, sql);
			effectRows += n;
			ClearParame();
			if (nextPage)
			{
				batchUpdates = batchUpdate.items.Skip(totalIndex);
				nextPage = false;
				if (batchUpdates.Any())
				{
					goto label1;
				}
			}
			return effectRows;
		}

		public override void InsertOrUpdate<TModel>(List<TModel> items, InsertOrUpdateOption option = null)
		{
			//超长处理
			var n = 0;
			var take = option?.BatchTake ?? 100;
			if (take == 0)
			{
				take = 100;
			}
			while (n < items.Count)
			{
				var parts = items.Skip(n).Take(take).ToList();
				_DBAdapter.InsertOrUpdate(dbContext, parts, option);
				n += parts.Count;
			}
		}
		#endregion
	}
}
